﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections;
using System.Diagnostics;
using WatiNCF.Automater.Business.Builders;
using WatiNCF;
using System.Configuration;
using WatiNCF.Automater.Business;
using WatiN.Core;
using WatiNCF.Automater.Controls;

namespace WatiNCF.Automater
{
    public partial class MainForm : System.Windows.Forms.Form
    {
        Exception _lastException;
        IHomePageController _controller;
        List<string> _urls;

        private static AutomaterConfigHandler GetConfig()
        {
            return ((AutomaterConfigHandler)ConfigurationManager.GetSection("automater"));
        }

        public IHomePageController Controller
        {
            get 
            {
                if (_controller == null)
                {

                    ControllerConfigElement element = GetConfig().Controllers[GetConfig().DefaultController];
                    if (element != null)
                    {
                        Type type = Type.GetType(string.Format("{0}, {1}", element.Type, element.Assembly), true);
                        _controller = (IHomePageController)Activator.CreateInstance(type, false);
                    }
                    else
                    {
                        throw new ApplicationException("HomePageController has not been defined. Check the app.config file.");
                    }
                }
                return _controller; 
            }
        }

        public List<string> Urls
        {
            get
            {
                if(_urls == null)
                {
                    _urls = new List<string>();
                    foreach (UrlConfigElement url in GetConfig().Urls)
                    {
                        _urls.Add(url.Path);
                    }
                }
                return _urls;
            }
        }

        public MainForm()
        {
            InitializeComponent();
        }

        public NotifyIcon NotifyIcon
        {
            get 
            {
                UpdateTaskBar(-1);
                this.LoadMenuItems();
                timer1.Enabled = true;
                return notifyIcon1; 
            }
        }

        //must be used to destroy the icon.
        [DllImport("user32.dll", EntryPoint = "DestroyIcon")]
        static extern bool DestroyIcon(IntPtr hIcon);

        //Pass in the number
        private void UpdateTaskBar(int i)
        {
            String TaskBarLetter;
            
            // Create a graphics instance that draws to a bitmap
            Bitmap bitmap = new Bitmap(16, 16);
            SolidBrush brush = new SolidBrush(fontDialog1.Color);
            Graphics graphics = Graphics.FromImage(bitmap);

            // Draw then number of Messages to the bitmap using the user selected font
            if (i != -1)
            {
                TaskBarLetter = i.ToString();
            }
            else
            {
                TaskBarLetter = "A";
            }
            graphics.DrawString(TaskBarLetter, fontDialog1.Font, brush, 0, 0);

            // Convert the bitmap into an icon and use it for the system tray icon
            IntPtr hIcon = bitmap.GetHicon();
            Icon icon = Icon.FromHandle(hIcon);
            notifyIcon1.Icon = icon;

            //GetHicon creates an unmanaged handle which must be manually destroyed
            DestroyIcon(hIcon);
        }


        // Hide the window if we're being minimized so it doesn't show in the
        // taskbar (will still show in the system tray).
        protected override void OnResize(EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
                this.Hide();
        }

        private void LoadMenuItems()
        {
            ProcessObject(new AutomationTreeBuilder().BuildAutomationTree(this.Controller));

            ToolStripSeparator separator = new ToolStripSeparator();
            contextMenuStrip1.Items.Add(separator);

            ToolStripMenuItem exitToolStripMenuItem = new ToolStripMenuItem("Exit");
            exitToolStripMenuItem.Click += new EventHandler(exitToolStripMenuItem_Click);
            contextMenuStrip1.Items.Add(exitToolStripMenuItem);
        }

        private void ProcessObject(AutomationItem target)
        {
            ProcessObject(contextMenuStrip1.Items, target);
        }

        private void ProcessObject(ToolStripItemCollection items, AutomationItem target)
        {
            ToolStripMenuItem menuItem = new ToolStripMenuItem(target.Text);
            menuItem.Tag = target.Tag;
            if (menuItem.Tag is AutomationMethod)
            {
                menuItem.Click += new EventHandler(methodItem_Click);
            }

            foreach (AutomationItem subItem in target.Items)
            {
                ProcessObject(menuItem.DropDownItems, subItem);
            }
            items.Add(menuItem);
        }

        

        void methodItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem methodItem = sender as ToolStripMenuItem;

            IE.Settings.MakeNewIeInstanceVisible = true;
            try
            {
                // Try and connect to an existing session
                bool attached = false;
                foreach (var url in Urls)
                {
                    try
                    {
                        IE.Settings.AttachToIETimeOut = 5;
                        IE.Settings.WaitForCompleteTimeOut = 15;
                        IE.Settings.WaitUntilExistsTimeOut = 15;
                        Controller.Browser = IE.AttachToIE(Find.ByUrl(new Regex(url)));
                        attached = true;
                    }
                    catch (WatiN.Core.Exceptions.IENotFoundException)
                    {
                        attached = false;
                    }
                }

                if (!attached)
                {
                    Controller.Browser = new IE();
                    Controller.NavigateToHomePage();
                }

                WalkItemTree(methodItem);

                notifyIcon1.ShowBalloonTip(5000, "Automation Complete", string.Format("Successfully completed {0}", methodItem.Text), ToolTipIcon.Info);
            }
            catch(Exception ex)
            {
                HandleError(ex);
            }
        }

        private void HandleError(Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            string message = ex.Message;
            if (ex.InnerException != null)
            {
                message = ex.InnerException.Message;
            }
            notifyIcon1.ShowBalloonTip(5000, "Automation Error", message, ToolTipIcon.Error);

            if (_lastException == null)
            {
                ToolStripMenuItem lastErrorMenuItem = new ToolStripMenuItem("Show Last Error");
                lastErrorMenuItem.Click += new EventHandler(OnLastErrorMenuItem_Click);
                contextMenuStrip1.Items.Add(lastErrorMenuItem);
            }

            _lastException = ex;
        }

        void OnLastErrorMenuItem_Click(object sender, EventArgs e)
        {
            ShowLastError();
        }

        void ShowLastError()
        {
            if (_lastException != null)
            {
                MessageBox.Show(string.Format("{0}.\r\n{1}", _lastException.Message, _lastException.ToString()));
            }
        }

        /// <summary>
        /// Traverses up the menu item's parent tree storing each method 
        /// in a stack, the methods are then executed in the correct order
        /// to perform the required action.
        /// </summary>
        /// <param name="menuItem">The menu item to walk.</param>
        private void WalkItemTree(ToolStripMenuItem menuItem)
        {
            Stack<AutomationMethod> stack = new Stack<AutomationMethod>();
            while(menuItem != null && menuItem.Tag is AutomationMethod)
            {
                menuItem = AddToStack(stack, menuItem);
            }

            while (stack.Count > 0)
            {
                Application.DoEvents();
                AutomationMethod autoMethod = stack.Pop();

                if (Controller is IRequiresLogin)
                {
                    if (autoMethod.LoginRequired)
                    {
                        ((IRequiresLogin) Controller).Login();
                    }
                    else
                    {
                        // Ensure the user is logged out
                        ((IRequiresLogin) Controller).Logout();
                    }
                }

                autoMethod.Method.Invoke(autoMethod.Target, HandleParameters(autoMethod));
            }
        }

        /// <summary>
        /// Handles the parameters required for the current method by prompting the user
        /// for input values.
        /// </summary>
        /// <param name="autoMethod">The auto method.</param>
        /// <returns></returns>
        private static object[] HandleParameters(AutomationMethod autoMethod)
        {
            object[] parameters = null;
            ParameterInfo[] paramInfo = autoMethod.Method.GetParameters();
            if (paramInfo.Count() > 0)
            {
                using (ParameterInputForm inputForm = DataEntryControlFactory.GetInputForm(autoMethod))
                {
                    if (inputForm.ShowDialog() == DialogResult.OK)
                    {
                        parameters = new object[paramInfo.Count()];
                        List<object> values = inputForm.GetParameterValues();
                        for (int i = 0; i < values.Count; i++)
                        {
                            try
                            {
                                parameters[i] = values[i];
                            }
                            catch
                            {
                                parameters[i] = null;
                            }
                        }
                    }
                }
            }
            return parameters;
        }

        private ToolStripMenuItem AddToStack(Stack<AutomationMethod> stack, ToolStripMenuItem menuItem)
        {
            AutomationMethod method = menuItem.Tag as AutomationMethod;
            if (method == null)
            {
                throw new ApplicationException("Reflected class is not an automation method");
            }

            stack.Push(method);

            return menuItem.OwnerItem as ToolStripMenuItem;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Hide();

            // Cancel the close...
            e.Cancel = true;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
        {
            ShowLastError();
        }
    }
}